アーカイブへ戻る

Claude Code ソースコード解説シリーズ 第9章: タスク管理

Claude Code が Todo、Plan、長期タスク状態をワークフローツールで追跡する方法を説明します。

『Claude Code ソースコード解析シリーズ』第9章|タスク管理

ここまでの数回はツールについて、主に「モデルがどのように外部世界と接するか」を扱ってきた。

  • ファイル系ツールはコードの読み書きを可能にする
  • 検索系ツールは手がかりを見つける手段を提供する
  • ターミナル系ツールはコマンドを実行させる
  • ネットワークと MCP ツールは外部の能力を取り込む

しかし、ファイルに触れず、コマンドも実行せず、リクエストも発行しない、もうひとつのカテゴリがある。それが答える問いはこうだ。

一度の作業が長くなったとき、Claude Code は自分がどこまで進めたのかをどう把握しているのか?

ユーザーからこんな一言が投げられた場面を想像してみてほしい。

ログイン失敗の不具合を修正して、テストを追加し、既存APIに影響がないか確認して、最後に修正点をまとめて。

これは明らかに単一ステップで済む話ではない。最低でも次のようなラウンドを踏む必要がある。

問題の特定
-> 修正方針の設計
-> コード修正
-> テスト実行
-> 失敗への対処
-> 結果の集約

すべての状態を会話テキストの中だけで管理しようとすると、モデルは簡単に次のような問題を起こす。

  • 途中で以前何を約束したか忘れてしまう
  • 完了済みの作業を繰り返してしまう
  • 依存関係を整理できず、後続タスクを先に実行してしまう
  • バックグラウンドでコマンドが走っているのに、メインの会話が出力の取得先を見失う
  • マルチエージェント協調時に、誰が何を担当するかすぐにわけがわからなくなる

そこで Claude Code には「ワークフロー系ツール」が必要になる。これらは特定の外部リソースを表すのではなく、エージェントの作業プロセスそのものを可視化・更新・再開可能な状態として扱う。

このツール群は4つの層で理解できる。

解決する問題典型的な形態
Todo現在の会話における未完了項目は何か軽量チェックリスト
Task複雑な作業をどう永続化・割り当て・依存関係の追跡・停止するかタスクリスト + ランタイムタスク
Plan実装着手前にどう読み取り専用の計画と承認へ移行するかPlan Mode / 計画ファイル / 権限制御
Worktree並行作業や高リスクな変更をどう分離するか独立した git worktree

本稿では Task を重点的に掘り下げる。というのも、これが最も「高機能になった Todo」と誤解されやすい一方で、ソースコード上では実際にまったく異なる二つの責務を担っているからだ。

1. なぜ Todo だけでは足りないのか

Todo は「これから自分がやるべきステップ」を表現するには向いている。直感的にはこういうものだ:

[ ] ログイン失敗を再現する
[ ] 失敗原因を特定する
[ ] auth ロジックを修正する
[ ] テストを追加する
[ ] 全テストを実行する

この種のチェックリストは短いタスクにはよく効く。ユーザーは Agent の進捗を把握できるし、モデル側も脱線を防ぐリマインダーとして使える。

しかし Todo には本質的な限界がある。それは「認知レベルの計画」を記述するものであって、「システムレベルのタスクオブジェクト」ではないという点だ。

タスクが複雑になると、途端にチェックリストは息切れし始める。

「テストを追加する」は「先に auth ロジックを修正する」ことに依存しているかもしれない。「全テストを実行する」はバックグラウンドコマンドで、結果が出るまで数分かかるかもしれない。さらにサブ Agent が関与し、あるレビュアーがセキュリティチェックを引き受け、別のワーカーが互換性テストを担当する——そんな状況になると、単なるチェックリストでは次のような問いに答えられなくなる:

  • この作業には一意な ID が振られているか?
  • 完全な説明はどこに保持されているか?
  • 誰が引き受けたのか?
  • どのタスクにブロックされているか?
  • 完了するとどのタスクのブロックが解除されるか?
  • バックグラウンドプロセスに対応する場合、出力はどこを読めばいいか?
  • バックグラウンドプロセスが停止した場合、どうやって止めるか?

Task は、Todo の書き方が下手だから生まれたわけではない。

Task が解決するのは次のことだ:

「やるべきこと」をチャット上の短い文から、ランタイムで管理可能なタスク状態へと昇格させる。

2. Task の第一層:タスクリスト —「何をすべきか」を管理する

Claude Code の Task が最も混乱しやすいのは、同名の仕組みが二層存在するためだ。

第一層は「タスクリスト Task」と呼ぶべきものだ。ここで扱うのは:

作業内容、担当者、現在の状態、他のタスクとの依存関係

ローカル wiki の要約が明確だ。TaskCreate / TaskGet / TaskUpdate / TaskList はバックグラウンドプロセスではなく、作業計画を管理するものである。関連資料は Claude Code Task ソース設計:タスクリストとランタイムタスク を参照。

Todo より本格的なタスクデータベースと考えればよい。

TaskCreate
-> pending 状態のタスクを 1 件作成
-> タスク ID、タイトル、説明、owner、依存関係、メタデータを書き込む
-> ローカルのタスクディレクトリに保存

こうしたタスクは通常、次の要素を含む。

id:タスク番号
subject:一行タイトル
description:タスクの詳細説明
activeForm:実行中にどう記述されるか
owner:担当者
status:pending / in_progress / completed
blocks:このタスクがブロックするタスク
blockedBy:このタスクをブロックしているタスク
metadata:追加のコンテキスト

Todo との違いは、Todo が「モデルに見せる短期リマインダー」に近いのに対し、Task は「ランタイムや協調システムに見せる作業オブジェクト」である点だ。

(ソースコード上でメモリ配列ではなく JSON ファイルで保存している理由は後述するが、本質的にはマルチプロセスでの同時書き込み競合を解決するためだ。)

簡単な例を示す。

ユーザー目標:ログイン失敗を修正する

#1 ログイン失敗を再現する
#2 auth ミドルウェアを確認する
#3 トークン更新ロジックを修正する
#4 リグレッションテストを追加する
#5 全テストを実行する

ここで #4#3 にブロックされ、#5#4 にブロックされる可能性があります。Todo を使えばモデルはこの関係を把握できますが、システム側で安定して管理し続けるのは困難です。タスクリスト Task なら、依存関係を構造化されたフィールドに書き込めます。

タスクリスト層の価値は「表示できる情報量が増える」ことではなく、作業を計算可能な状態に変換できることです。

まだ着手していないタスクはどれか
いま実行中のタスクはどれか
完了したタスクはどれか
ブロックされているタスクはどれか
現在のタスクを完了すると何がアンロックされるか
特定の owner がいま担当しているタスクはどれか

これはマルチエージェント協調においても非常に重要な層です。teammate、worker、reviewer が参加するようになると、タスクをメインエージェントの頭の中だけに置いておくわけにはいきません。全員が読み書きできる共有プロトコルに落とし込まなければならないのです。

3. タスクリストをファイルシステムに置く理由

ソースコードの設計を見ると、Claude Code はタスクリストをメモリ上の配列として持つのではなく、各タスクを独立した JSON ファイルとして保存している。

一見「重たい」設計に見えるが、これにはエンジニアリング上の判断がある。

(すべてのタスクをひとつのグローバル配列で管理すると、マルチエージェントやマルチプロセスでの並行更新時に、細粒度のロックが極めて難しい。レビュアーが #4 のステータスを更新するのと同時に、ワーカーが #3 の説明を更新するケースで、タスク単位の分離がないと、最後に書き込んだ側が前の更新を簡単に上書きしてしまう。)

タスクごとに1ファイルとすることで、並行更新の衝突を単一タスクのレベルに閉じ込められる:

tasks/
  task-list-id/
    .highwatermark
    1.json
    2.json
    3.json

.highwatermark は割り当て済みの最大 ID を記録し、タスク削除後の ID 再利用を防ぐ。リストレベルのロックは作成・リセット・引き受けといった全体操作を管理し、タスクレベルのロックは個別タスクの更新を管理する。

これはつまり、Task システムは UI にリストを追加しているのではなく、実際のコラボレーションにおける状態の一貫性問題を解決しているのだ。

両者の違いを一言で覚えるなら:

Todo は対話の中のワーキングメモリであり、タスクリスト Task はファイルシステム上のワークの事実である。

4. Task の第二層:ランタイムタスク、「誰が今走っているか」を管理する

ここまでは Task の第一層に過ぎない。

Claude Code には第二層も存在し、同じく Task と呼ばれるが、こちらが関心を持つのは「何をすべきか」ではない。

現在、バックグラウンドで実行中の処理体がどれだけあり、その出力はどこにあるのか、必要に応じてどう停止するのか。

これがランタイム Task である。

たとえばモデルがテストコマンドを実行したとしよう。

npm test

このコマンドがすぐに終了するなら、それは一度きりの Bash 呼び出しにすぎない。しかしバックグラウンドタスクになった場合、メインの対話は先に進みつつ、そのプロセスに対する制御権を保持しなければならない。

バックグラウンドテストタスクが実行中
→ メイン Agent はコードを見続けるか、ユーザーの入力を待つ
→ しばらくしてテスト出力を読み取る
→ スタックしたらタスクを停止する

ランタイム Task が管理するのはまさにこうしたオブジェクトだ。これらは現在のセッションの AppState.tasks に格納され、次の型を持つ。

local_bash
local_agent
remote_agent
in_process_teammate
local_workflow
monitor_mcp
dream

状態もタスクリストとは異なる。

pending
running
completed
failed
killed

注目すべきは、もはや pending / in_progress / completed ではないという点だ。ランタイム Task はワークプランの状態というより、むしろプロセスのライフサイクルに近い。

したがって、TaskStopTaskOutput はタスクリストの層に属さない。

TaskOutput が関心を持つのは次のようなことだ。

このバックグラウンドタスクに出力はあるか?
前回どこまで読み取ったか?
今回はブロッキング待機すべきか?
出力ファイルは上限を超えていないか?

TaskStop が関心を持つのは次のようなことだ。

このタスクは存在するか?
まだ running 状態か?
どのタイプのバックグラウンドタスクか?
どの kill 実装を呼び出すべきか?

これは TaskCreate とはまったく別物なのである。

5. 同じ仕事になぜ二つの ID があるのか

二層の Task で最も混乱しやすいのは、現実の同一作業に対して二つの ID が同時に存在しうる点だ。

タスクリストに次の項目があるとする。

#5 全テスト実行

モデルが実行を開始し、Bash を呼び出してテストを起動する。テストがバックグラウンドに入ると、システムはさらにランタイムタスクを生成する。

b9x4k2...  local_bash  running

同じ事象に、二つの身分が存在する。

身分表すもの利用者
#5計画上の「全テスト実行」という作業ユーザー、メイン Agent、teammate
b9x4k2...実行中のテストプロセスランタイム、TaskOutput、TaskStop

前者は「作業が完了したか」に答え、後者は「プロセスが今どうなっているか」に答える。

テストが失敗した場合、モデルは b9x4k2... の出力を読み取り、#5in_progress のままにするか、関連タスクを更新する。テストが成功すれば、#5completed にする。

二層の Task を統合できない理由は次のとおりだ。

タスクリスト Task:コラボレーション状態
ランタイム Task:実行ライフサイクル

一方は「何をしようとしているか、どこまで進んだか」を記述し、もう一方は「システム上で誰が実行中か、出力はどこか、どう停止するか」を記述する。

6. Task ツールの役割分担

二層に分けると、ツールの名前が明確になる。

タスクリスト層:

ツール役割
TaskCreate計画タスクを作成する(実行は開始しない)
TaskGet特定タスクの完全な説明、ステータス、依存関係を読み取る
TaskListタスクのサマリーを一覧表示し、モデルやチームメイトが現在の作業を把握できるようにする
TaskUpdateステータス、オーナー、メタデータ、依存関係の更新、またはタスクの削除

ランタイム層:

ツール役割
TaskOutputバックグラウンドタスクの出力を読み取る(増分読み取りと待機に対応)
TaskStop実行中のバックグラウンドタスクを停止する

これらを混同すると、次のような落とし穴にはまる:

  • TaskCreate で作成した #1TaskStop で停止できない
  • TaskOutputTaskCreate の description を読み取れない
  • TaskUpdate が更新するのはタスクリストの状態であり、バックグラウンドプロセスの状態ではない
  • バックグラウンドプロセスが失敗しても、対応する計画タスクが自動的に完了したり削除されたりするわけではない

この点はソースコードを読む上で重要だ。Task という名前を見かけたら、まずこう自問しよう:

ここで言っているのはタスクリストか、それともランタイムの実行体か?

7. Task と Agent の協調関係

Task システムがワークフロー系ツールに分類されているのには、もうひとつ理由がある。それは、マルチ Agent 協調の土台となるものだからだ。

シングル Agent の時代であれば、タスク状態は粗くても構わない。主 Agent が自分でやるべきことを把握していればよく、せいぜい Todo を管理する程度で済む。

しかし、マルチ Agent 時代に入ると事情が変わる。

主 Agent:全体目標と最終判断を担う
worker:特定モジュールの実装を担う
reviewer:リスクの審査を担う
tester:テスト実行と再現を担う

このとき「タスク」は以下の要件を支えなければならない。

  • 特定の owner が引き受けられること
  • 異なる参加者に提示できること
  • 依存関係を表現できること
  • 完了時に利害関係者へ通知できること
  • チームメイトの離脱時に未完了タスクを解放できること
  • バックグラウンドの agent やシェルを実行時管理に組み込めること

つまり、Task は孤立したツールではない。Agent、Plan、Bash、権限、UI のすべてと結びついている。

全体像を簡略図で示す。

05.3工作流工具-任务管理 図 1

タスクリストと実行時タスクは、一方が他方を代替する関係ではなく、協調関係にある。

タスクリストはチームに「何をすべきか」を知らせ、実行時タスクはシステムに「誰が今走っているか」を知らせる。

8. Plan・Worktreeとの境界

TaskはPlanやWorktreeと一緒に登場することが多いが、三者が解決する問題は異なる。

Planが解決するのは、手を動かす前の境界だ:

最初にリードオンリーで調査
→ 計画を生成
→ ユーザーの承認を待つ
→ 実行フェーズへ

Taskが解決するのは、実行途中の状態管理だ:

どの作業項目が存在するか
→ 誰が担当か
→ どれが完了したか
→ バックグラウンド実行体がどのように出力を読み取り停止するか

Worktreeが解決するのは、ファイル変更の隔離だ:

並行変更やリスクの高い変更
→ 独立したワークスペースを作成
→ メインワークツリーの汚染を防ぐ
→ 最終的にマージまたは破棄

三者の関係をひと続きにすると:

Plan:最初にどう進めるかを決める
Task:やるべきことを追跡可能な状態に落とし込む
Worktree:実際にコードを変更する実行段階に隔離された作業領域を提供する

Todoはより軽量で、多くの場合、現在の会話の中でユーザーとモデルに向けた進捗表示として使われるにすぎない。

9. ソースコードを読むときに意識すべき一本の線

Claude Code の Task 関連ソースコードを読むときは、ツール名を丸暗記するよりも、次の二つの問いを持ちながら読み進めることをおすすめします。

第一の問い:このコードは「ワークプラン」を管理しているのか、それとも「バックグラウンド実行体」を管理しているのか?
第二の問い:この状態は誰に向けたものか——モデルか、ユーザーか、teammate か、それともランタイムか?

タスクリスト層で重点的に見るファイル:

  • src/utils/tasks.ts
  • TaskCreateTool
  • TaskGetTool
  • TaskUpdateTool
  • TaskListTool
  • useTasksV2
  • TaskListV2

ランタイム層で重点的に見るファイル:

  • src/Task.ts
  • src/tasks.ts
  • src/tasks/stopTask.ts
  • LocalShellTask
  • LocalAgentTask
  • TaskStopTool
  • TaskOutputTool
  • diskOutput
  • framework

TaskCreate だけを眺めると「Task は高機能な Todo に過ぎない」と思えてしまいます。 TaskStop だけを眺めると「Task はバックグラウンドプロセスに過ぎない」と思えてしまいます。

両方の層を重ねて見て初めて、Claude Code の本当の設計思想が浮かび上がります。

モデルに「自分が何をすべきか」を覚えさせる仕組みではない。ホストシステムに対して、観測可能・割り当て可能・依存可能・停止可能・再通知可能なワークステートを提供する仕組みなのである。

10. まとめ

Task はワークフローツール群の中で最も中核的でありながら、最も誤解されやすい要素です。

Todo の強化版ではなく、二つの機構の組み合わせです。

  • タスクリスト Task:計画項目を、永続化・割り当て可能・依存関係を持てる作業オブジェクトに変える。
  • ランタイム Task:バックグラウンドシェル、エージェント、リモートセッションなどの実行体を、読み取り・停止・通知が可能なライフサイクルオブジェクトに変える。

この点を理解すれば、Claude Code の長時間タスク実行能力もすんなり腑に落ちます。

Todo    → 短期的な手順を見える化する
Plan    → 実行前の理解と承認をコントロールする
Task    → 実行プロセスを追跡可能にする
Worktree → ファイル変更を分離する

これらの層が組み合わさることで、Claude Code は単なる「次に何をするか」を話すチャットアシスタントではなく、エンジニアリング作業を組織化できるランタイムとして機能するのです。